﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using UtilZ.Dotnet.Ex.ZLog;

namespace UtilZ.Dotnet.Ex.Communication.Net.Web
{
    internal partial class ApiMethod
    {
        /// <summary>
        /// 调用api对象中的相应方法
        /// </summary>
        /// <param name="request"></param>
        /// <param name="apiParaValueList">api方法参数列表</param>
        /// <returns></returns>
        private IHttpResponse PrimitiveApiInvoke(HttpRequest request, SortedList<int, object> apiParaValueList)
        {
            var response = new HttpResponse(request.Connection);
            try
            {
                response.TextEncoding = request.GetClientTextEncoding();

                //验证是否有body参数
                if (this._apiBodyParameterInfo != null)
                {
                    var paraValue = this.GetFromBodyParameterValue(request, this._apiBodyParameterInfo);
                    if (apiParaValueList == null)
                    {
                        apiParaValueList = new SortedList<int, object>();
                    }
                    apiParaValueList.Add(this._apiBodyParameterInfo.ParameterInfo.Position, paraValue);
                }

                //检查是否需要HttpActionContext参数
                HttpActionContext actionContext = null;
                if (this._httpInfoParameterInfo != null)
                {
                    actionContext = new HttpActionContext(request, response);
                    if (apiParaValueList == null)
                    {
                        apiParaValueList = new SortedList<int, object>();
                    }
                    apiParaValueList.Add(this._httpInfoParameterInfo.ParameterInfo.Position, actionContext);
                }

                //验证参数个数是否正确
                if (apiParaValueList != null && apiParaValueList.Count != this._apiParameterInfos.Count)
                {
                    response.StatusCode = HttpStatusCodes.BadRequest;
                    return response;
                }

                //调用api方法
                object[] parameters = null;
                if (apiParaValueList != null)
                {
                    parameters = apiParaValueList.Values.ToArray();
                }

                object result;
                if (this._obj != null)
                {
                    result = this._methodInfo.Invoke(this._obj, parameters);
                }
                else if (this._mapDelegatede != null)
                {
                    result = this._mapDelegatede.DynamicInvoke(parameters);
                }
                else
                {
                    throw new ApplicationException($"Api方法\"{request.OriginUrl}\"没有调用目标");
                }

                //释放HttpFormData,暂时先写这儿，以后再重构更好的方式
                if (parameters != null)
                {
                    HttpFormData item;
                    foreach (var parameter in parameters)
                    {
                        item = parameter as HttpFormData;
                        if (item != null)
                        {
                            item.Dispose();
                        }
                    }
                }

                //查检响应数据是否已通过HttpInfo设置好值
                if (actionContext != null && actionContext.Handled)
                {
                    return actionContext.Response;
                }

                //设置结果值到http响应
                IHttpDataConverter convert = this._methodAttribute.Converter;
                if (convert != null)
                {
                    //该api方法允许自定义设置响应数据,采用自定义方式设置响应数据
                    convert.Write(response, result);
                    return response;
                }
                else
                {
                    //采用内置方式设置响应数据
                    this.SetResponseValue(request, response, result);
                }
            }
            catch (HttpArgumentException haex)
            {
                ZLoger.Warn(haex);
                //response.StatusCode = HttpStatusCodes.PreconditionFailed;
                response.StatusCode = HttpStatusCodes.BadRequest;
            }
            catch (HttpException hex)
            {
                ZLoger.Warn(hex);
                response.StatusCode = hex.StatusCode;
            }
            catch (Exception ex)
            {
                ZLoger.Error(ex);
                response.StatusCode = HttpStatusCodes.InternalServerError;
            }

            return response;
        }

        private void SetResponseValue(HttpRequest request, HttpResponse response, object result)
        {
            if (result is IApiActionResult)
            {
                ((IApiActionResult)result).SetResponse(request, response);
            }
            else if (result is byte[])
            {
                response.SetBytes((byte[])result);
            }
            else if (result is string)
            {
                response.SetTextContent(HttpContentTypes.TEXT, (string)result);
            }
            else
            {
                this.SetNoSerializeResponseValue(request, response, result);
            }
        }

        private void SetNoSerializeResponseValue(HttpRequest request, HttpResponse response, object result)
        {
            string text;
            if (request.Headers.TryGetValue(HttpRequestHeaders.Accept, out string accept))
            {
                //优先json格式
                if (accept.Contains(HttpContentTypes.ANY) || accept.Contains(HttpContentTypes.JSON))
                {
                    text = this._serializer.JsonSerialize(result);
                    response.SetJSON(text);
                }
                else if (accept.Contains(HttpContentTypes.XML))
                {
                    text = this._serializer.XmlSerialize(result);
                    response.SetXml(text);
                }
                else
                {
                    text = this._serializer.JsonSerialize(result);
                    response.SetJSON(text);
                }
            }
            else
            {
                text = this._serializer.JsonSerialize(result);
                response.SetJSON(text);
            }
        }



        private object GetFromBodyParameterValue(HttpRequest request, ApiParameterInfo apiParameterInfo)
        {
            object bodyParaValue;
            try
            {
                IHttpDataConverter convert = this._methodAttribute.Converter;
                //不为null则尝试自定义读取数据
                if (convert != null && convert.TryRead(request, out bodyParaValue))
                {
                    return bodyParaValue;
                }

                //上传文件
                if (apiParameterInfo.ParameterInfo.ParameterType == typeof(HttpFormData))
                {
                    return request.ReceiveHttpFormData();
                }

                //提交的数据内容
                string body = request.GetBody();
                if (apiParameterInfo.ParaTypeCode == TypeCode.String)
                {
                    //body参数类型为字符串,直接返回
                    return body;
                }

                string err;
                if (request.Headers.TryGetValue(HttpRequestHeaders.ContentType, out var contentType))
                {
                    //根据ContentType使用相应的方法序列化解析数据

                    //优先json
                    if (contentType.Contains(HttpContentTypes.ANY) ||
                        contentType.Contains(HttpContentTypes.JSON))
                    {
                        bodyParaValue = this._serializer.JsonDeserialize(body, apiParameterInfo.ParameterInfo.ParameterType);
                    }
                    else if (contentType.Contains(HttpContentTypes.XML))
                    {
                        bodyParaValue = bodyParaValue = this._serializer.XmlDeserialize(body, apiParameterInfo.ParameterInfo.ParameterType);
                    }
                    else
                    {
                        err = $"api{this._methodInfo.DeclaringType.FullName}.{this._methodInfo.Name}{HttpRequestHeaders.ContentType}\"{contentType}\"不支持";
                        throw new HttpArgumentException(err);
                    }
                }
                else
                {
                    try
                    {
                        //没有ContentType,则通过强制转换的方式,将body转换为body参数类型
                        bodyParaValue = Convert.ChangeType(body, apiParameterInfo.ParameterInfo.ParameterType);
                    }
                    catch (Exception ex)
                    {
                        err = $"api{this._methodInfo.DeclaringType.FullName}.{this._methodInfo.Name}参数{apiParameterInfo.ParameterInfo.Name}值\"{body}\"转换为\"{apiParameterInfo.ParameterInfo.ParameterType.FullName}\"失败";
                        throw new HttpArgumentException(err, ex);
                    }
                }
            }
            catch (HttpArgumentException)
            {
                throw;
            }
            catch (HttpException)
            {
                throw;
            }
            catch (Exception ex)
            {
                throw new HttpArgumentException(ex.Message);
            }

            return bodyParaValue;
        }








        /// <summary>
        /// 完全匹配路由地址调用
        /// </summary>
        /// <param name="request">请求</param>
        /// <returns>IHttpResponse</returns>
        public IHttpResponse Invoke(HttpRequest request)
        {
            return this.PrimitiveApiInvoke(request, null);
        }





        /// <summary>
        /// 尝试匹配配路由地址调用[匹配成功返回true,失败返回flase]
        /// </summary>
        /// <param name="request">请求</param>
        /// <param name="response">响应结果</param>
        /// <returns>匹配成功返回true,失败返回flase</returns>
        public bool TryInvoke(HttpRequest request, out IHttpResponse response)
        {
            if (this._tempParaApiFixUrlPrefix != null &&
                !request.OriginUrl.StartsWith(this._tempParaApiFixUrlPrefix, StringComparison.OrdinalIgnoreCase))
            {
                //请求地址不以模板参数固定部分地址开始,显然不匹配
                response = null;
                return false;
            }

            bool result;
            switch (this._apiMethodType)
            {
                case ApiMethodTypes.FixUrl:
                    //固定模板直接调用了,设计层面上,是不会进入此分支的,如果有,就是服务器内部逻辑错误
                    response = null;
                    result = false;
                    break;
                case ApiMethodTypes.QueryStringPara:
                    //QueryString参数模板 eg:test?min={timestampMin}&max={timestampMax}
                    result = this.TryInvokeQueryStringTemplate(request, out response);
                    break;
                case ApiMethodTypes.UrlPara:
                    //url参数模板eg:{z}/{x}/{y}.png 或 abc{z}/gg{x}mm/{y}.png
                    result = this.TryInvokeUrlTemplate(request, out response);
                    break;
                case ApiMethodTypes.Mix:
                    //混合参数模板 eg:{z}/{x}/{y}.png?id=123&ts=456
                    result = this.TryInvokeMixTemplate(request, out response);
                    break;
                default:
                    throw new NotImplementedException(this._apiMethodType.ToString());
            }

            return result;
        }

        private bool TryInvokeMixTemplate(HttpRequest request, out IHttpResponse response)
        {
            response = null;

            SortedList<int, object> apiParaValueList = new SortedList<int, object>();//用于api调用
            List<object> urlParaValueList = new List<object>();//用于验证url是否正确

            //获取url模板参数
            bool result = this.GetUrlTemplatePara(request, this._urlTemplateApiTemplatePara.UrlSegments, apiParaValueList, urlParaValueList);
            if (!result)
            {
                return false;
            }

            if (urlParaValueList.Count != this._urlTemplateApiTemplatePara.ParaCount)
            {
                //参数数量不够
                return false;
            }

            //url模板参数数量
            int urlTemplateParaCount = urlParaValueList.Count;

            //获取queryString模板参数
            result = this.GetQueryStringTemplatePara(request, this._queryStringTemplateApiTemplatePara.UrlSegments, apiParaValueList, urlParaValueList);
            if (!result)
            {
                return false;
            }

            if ((urlParaValueList.Count - urlTemplateParaCount) != this._queryStringTemplateApiTemplatePara.ParaCount)
            {
                //参数数量不够
                return false;
            }

            //url参数数量不同,拿到默认值后,再先进行验证,比较uel与期望的url是否相同
            string url = string.Format(this._routUrl, urlParaValueList.ToArray());
            if (!this.CompareUrl(request.OriginUrl, url))
            {
                return false;
            }

            response = this.PrimitiveApiInvoke(request, apiParaValueList);
            return true;
        }



        private bool TryInvokeUrlTemplate(HttpRequest request, out IHttpResponse response)
        {
            response = null;

            SortedList<int, object> apiParaValueList = new SortedList<int, object>();//用于api调用
            List<object> urlParaValueList = new List<object>();//用于验证url是否正确
            bool result = this.GetUrlTemplatePara(request, this._urlTemplateApiTemplatePara.UrlSegments, apiParaValueList, urlParaValueList);
            if (!result)
            {
                return false;
            }

            if (urlParaValueList.Count != this._urlTemplateApiTemplatePara.ParaCount)
            {
                //参数数量不够
                return false;
            }

            //url参数数量不同,拿到默认值后,再先进行验证,比较uel与期望的url是否相同
            string url = string.Format(this._routUrl, urlParaValueList.ToArray());
            if (!this.CompareUrl(request.OriginUrl, url))
            {
                return false;
            }

            response = this.PrimitiveApiInvoke(request, apiParaValueList);
            return true;
        }

        /// <summary>
        /// 获取url模板参数
        /// </summary>
        /// <param name="request"></param>
        /// <param name="urlSegments">url参数集合</param>
        /// <param name="apiParaValueList">api调用参数集合</param>
        /// <param name="urlParaValueList">验证url参数是否正确用的参数集合</param>
        /// <returns></returns>
        private bool GetUrlTemplatePara(HttpRequest request, ReadOnlyCollection<UrlSegment> urlSegments, SortedList<int, object> apiParaValueList, List<object> urlParaValueList)
        {
            // http://192.168.10.96:23018/testApi/6/10/50.png
            // [HttpGet("{z}/{x}/{y}.png")]  testApi/{z}/{x}/{y}.png

            //url模板中,如果url中包含?,则说明有querystring参数,是不该处理的请求
            if ((this._routUrl.Contains(WebConstnats.QUESTION_MARK) || request.OriginUrl.Contains(WebConstnats.QUESTION_MARK)) &&
                this._apiMethodType != ApiMethodTypes.Mix)
            {
                return false;
            }

            if (urlSegments == null || urlSegments.Count == 0)
            {
                //参数在url中,但是实际上没有参数,不符合
                return false;
            }

            string paraString = request.Url.Substring(this._routTemplate.Length);
            string[] paraStrArr = paraString.Split(WebConstnats.PATH_SEPARATOR_ARR, StringSplitOptions.RemoveEmptyEntries);
            if (urlSegments.Count != paraStrArr.Length)
            {
                //参数在url中,url片数不同,不符合
                return false;
            }


            object paraValue;
            string urlSliceStr, paraValueStr;
            int subLen;
            UrlSegment urlSegment;

            for (int i = 0; i < urlSegments.Count; i++)
            {
                urlSegment = urlSegments[i];
                if (urlSegment.ApiParameterInfo == null)
                {
                    //固定url段

                    continue;
                }

                if (urlSegment.ApiParameterInfo.TemplateParameterType == ApiParameterValueSourceTypes.None ||
                    urlSegment.ApiParameterInfo.TemplateParameterType == ApiParameterValueSourceTypes.FromQueryString)
                {
                    continue;
                }

                if (i < paraStrArr.Length)
                {
                    urlSliceStr = paraStrArr[i];
                    if (string.IsNullOrWhiteSpace(urlSegment.ParaPrefix))
                    {
                        if (string.IsNullOrWhiteSpace(urlSegment.ParaSuffix))
                        {
                            paraValueStr = urlSliceStr;
                        }
                        else
                        {
                            subLen = urlSliceStr.Length - urlSegment.ParaSuffix.Length;
                            if (subLen < 0)
                            {
                                //url片长度小于了该片后缀字符串长度, 参数无效
                                return false;
                            }

                            paraValueStr = urlSliceStr.Substring(0, subLen);
                        }
                    }
                    else
                    {
                        if (string.IsNullOrWhiteSpace(urlSegment.ParaSuffix))
                        {
                            if (urlSliceStr.Length < urlSegment.ParaPrefix.Length)
                            {
                                //url片长度小于了该片前缀字符串长度, 参数无效
                                return false;
                            }

                            paraValueStr = urlSliceStr.Substring(urlSegment.ParaPrefix.Length);
                        }
                        else
                        {
                            subLen = urlSliceStr.Length - urlSegment.ParaPrefix.Length - urlSegment.ParaSuffix.Length;
                            if (subLen < 0)
                            {
                                //url片长度小于了该片字符串前和缀后缀字符串长度之和, 参数无效
                                return false;
                            }

                            paraValueStr = urlSliceStr.Substring(urlSegment.ParaPrefix.Length, subLen);
                        }
                    }

                    if (urlSegment.ApiParameterInfo.ParaTypeCode != TypeCode.String)
                    {
                        try
                        {
                            paraValue = Convert.ChangeType(paraValueStr, urlSegment.ApiParameterInfo.ParameterInfo.ParameterType);
                            apiParaValueList.Add(urlSegment.ApiParameterInfo.ParameterInfo.Position, paraValue);
                            urlParaValueList.Add(paraValue);
                        }
                        catch (Exception ex)
                        {
                            ZLoger.Warn(ex, $"参数{urlSegment.UrlParaName}值\"{paraValueStr}\"转换为\"{urlSegment.ApiParameterInfo.ParameterInfo.ParameterType.FullName}\"失败");
                            return false;
                        }
                    }
                    else
                    {
                        paraValue = paraValueStr;
                        apiParaValueList.Add(urlSegment.ApiParameterInfo.ParameterInfo.Position, paraValue);
                        urlParaValueList.Add(paraValue);
                    }
                }
                else
                {
#if NET4_0
                    if (urlSegment.ApiParameterInfo.ParameterInfo.DefaultValue != null)
#else
                    if (urlSegment.ApiParameterInfo.ParameterInfo.HasDefaultValue)
#endif
                    {
                        paraValue = urlSegment.ApiParameterInfo.ParameterInfo.DefaultValue;
                        apiParaValueList.Add(urlSegment.ApiParameterInfo.ParameterInfo.Position, paraValue);
                        urlParaValueList.Add(paraValue);
                    }
                    else
                    {
                        //需要的参数不存在,且无默认值
                        return false;
                    }
                }
            }

            return true;
        }



        private bool TryInvokeQueryStringTemplate(HttpRequest request, out IHttpResponse response)
        {
            //http://192.168.10.96:23018/testApi/test?min=123&max=456
            //[HttpGet("test?min={timestampMin}&max={timestampMax}")]
            response = null;

            SortedList<int, object> apiParaValueList = new SortedList<int, object>();//用于api调用
            List<object> urlParaValueList = new List<object>();//用于验证url是否正确
            bool result = this.GetQueryStringTemplatePara(request, this._queryStringTemplateApiTemplatePara.UrlSegments, apiParaValueList, urlParaValueList);
            if (!result)
            {
                return false;
            }

            if (urlParaValueList.Count != this._queryStringTemplateApiTemplatePara.ParaCount)
            {
                //参数数量不够
                return false;
            }

            //验证url是否匹配
            string url = string.Format(this._routUrl, urlParaValueList.ToArray());
            if (!this.CompareUrl(request.OriginUrl, url))
            {
                return false;
            }

            //调用api方法
            response = this.PrimitiveApiInvoke(request, apiParaValueList);
            return true;
        }

        /// <summary>
        /// 获取QueryString模板参数
        /// </summary>
        /// <param name="request"></param>
        /// <param name="urlSegments">url参数集合</param>
        /// <param name="apiParaValueList">api调用参数集合</param>
        /// <param name="urlParaValueList">验证url参数是否正确用的参数集合</param>
        /// <returns></returns>
        private bool GetQueryStringTemplatePara(HttpRequest request, ReadOnlyCollection<UrlSegment> urlSegments, SortedList<int, object> apiParaValueList, List<object> urlParaValueList)
        {
            //http://192.168.10.96:23018/testApi/test?min=123&max=456
            //[HttpGet("test?min={timestampMin}&max={timestampMax}")]

            //QueryString模板中,如果url中不包含?,则说明没有querystring参数,是不该处理的请求
            if (!request.OriginUrl.Contains(WebConstnats.QUESTION_MARK))
            {
                return false;
            }

            if (urlSegments == null || urlSegments.Count == 0)
            {
                //如果无参数,则是硬url,不符合
                return false;
            }

            var paraValueDic = request.QueryStringParams;
            if (paraValueDic == null)
            {
                //有参数,但是数量不匹配,不符合
                return false;
            }

            object paraValue;
            string paraValueStr;
            UrlSegment urlSegment;

            for (int i = 0; i < urlSegments.Count; i++)
            {
                urlSegment = urlSegments[i];
                if (paraValueDic.TryGetValue(urlSegment.UrlParaName, out paraValueStr))
                {
                    if (urlSegment.ApiParameterInfo.ParaTypeCode != TypeCode.String)
                    {
                        try
                        {
                            paraValue = Convert.ChangeType(paraValueStr, urlSegment.ApiParameterInfo.ParameterInfo.ParameterType);
                            apiParaValueList.Add(urlSegment.ApiParameterInfo.ParameterInfo.Position, paraValue);
                            urlParaValueList.Add(paraValue);
                        }
                        catch (Exception ex)
                        {
                            ZLoger.Warn(ex, $"参数{urlSegment.UrlParaName}值\"{paraValueStr}\"转换为\"{urlSegment.ApiParameterInfo.ParameterInfo.ParameterType.FullName}\"失败");
                            return false;
                        }
                    }
                    else
                    {
                        paraValue = paraValueStr;
                        apiParaValueList.Add(urlSegment.ApiParameterInfo.ParameterInfo.Position, paraValue);
                        urlParaValueList.Add(paraValue);
                    }
                }
                else
                {
#if NET4_0
                    if (urlSegment.ApiParameterInfo.ParameterInfo.DefaultValue != null)
#else
                    if (urlSegment.ApiParameterInfo.ParameterInfo.HasDefaultValue)
#endif
                    {
                        paraValue = urlSegment.ApiParameterInfo.ParameterInfo.DefaultValue;
                        apiParaValueList.Add(urlSegment.ApiParameterInfo.ParameterInfo.Position, paraValue);
                        urlParaValueList.Add(paraValue);
                    }
                    else
                    {
                        //需要的参数不存在,且无默认值
                        return false;
                    }
                }
            }

            return true;
        }

        /// <summary>
        /// 比较两个url是否相同[相同返回true,不同返回false]
        /// </summary>
        /// <param name="url1">url1</param>
        /// <param name="url2">url2</param>
        /// <returns>相同返回true,不同返回false</returns>
        private bool CompareUrl(string url1, string url2)
        {
            return string.Equals(url1.Trim(), url2.Trim(), StringComparison.OrdinalIgnoreCase);
        }
    }
}
